# -*- coding:utf-8  -*-
# @Author: ChenYangMing
# @Time: 2024-09-25

import numpy as np
import pandas as pd

from Common.eva_utils import Evaluator


class TaskEvaluator(Evaluator):
    """
    任务评估器类，用于评估时间序列预测任务的性能。
    """
    def __init__(self, y_true, y_pred):
        """
        初始化任务评估器。
        :param y_true: pd.DataFrame, 真实的标签。
        :param y_pred: pd.DataFrame, 预测的标签。
        """
        super().__init__(y_true, y_pred)

    def evaluate(self):
        """
        评估模型的性能，计算综合评分。
        :return: float, 综合评分，取值范围[0, 1]。
        """
        self.y_true['时间'] = pd.to_datetime(self.y_true['时间'])
        self.y_pred['时间'] = pd.to_datetime(self.y_pred['时间'])
        self.y_pred['地市'] = self.y_pred['地市'].str.extract(r'city(.*)', expand=False)
        merge_df = pd.merge(self.y_true, self.y_pred, on=['地市', '时间'], how='left')
        merge_df = merge_df[merge_df['流量_x'].isnull() == False]
        hour_index = self.cal_hour_index(merge_df['流量_x'], merge_df['流量_y'])
        daily_peak_index = self.cal_daily_peak_index(merge_df)
        holidays_hour_index = self.cal_holidays_hour_index(merge_df)
        return round(hour_index * 0.7 + daily_peak_index * 0.2 + holidays_hour_index * 0.1, 4)

    @staticmethod
    def cal_mape(y, pred):
        """
        计算平均绝对百分比误差（MAPE）。
        :param y: pd.Series, 真实值序列。
        :param pred: pd.Series, 预测值序列。
        :return: float, 计算出的MAPE值。
        """
        y = np.array(y)
        pred = np.array(pred)
        mape = np.mean(np.abs((y - pred) / y)) * 100
        return mape

    def cal_hour_index(self, y, pred):
        """
        计算小时级别的预测准确度评分。
        :param y: pd.Series, 真实流量值序列。
        :param pred: pd.Series, 预测流量值序列。
        :return: float, 小时级别的评分。
        """
        hour_index = self.cal_mape(y, pred)
        return hour_index

    def cal_daily_peak_index(self, merge_df):
        """
        计算日高峰时段的预测准确度评分。
        :param merge_df: pd.DataFrame, 合并后的真实和预测数据。
        :return: float, 日高峰时段的评分。
        """
        y_peak = merge_df.groupby(['地市', merge_df['时间'].dt.date])['流量_x'].max().reset_index()
        pred_peak = merge_df.groupby(['地市', merge_df['时间'].dt.date])['流量_y'].max().reset_index()
        daily_peak_index = self.cal_mape(y_peak['流量_x'], pred_peak['流量_y'])
        return daily_peak_index

    def cal_holidays_hour_index(self, merge_df):
        """
        计算节假日的预测准确度评分。
        :param merge_df: pd.DataFrame, 合并后的真实和预测数据。
        :return: float, 节假日的评分。
        """
        # holidays_t = [['2018/12/30 0:00:00', '2019/1/1 23:00:00'], ['2019/2/4 0:00:00', '2019/2/10 23:00:00'],
        #               ['2019/5/1 0:00:00', '2019/5/4 23:00:00'], ['2019/9/13 0:00:00', '2019/9/15 23:00:00'],
        #               ['2019/10/1 0:00:00', '2019/10/7 23:00:00']]
        holidays_t = [['2018/12/30 0:00:00', '2019/1/1 23:00:00'], ['2019/2/4 0:00:00', '2019/2/10 23:00:00'],
                      ['2019/5/1 0:00:00', '2019/5/4 23:00:00']]  # 测评的节假日包括：元旦、春节、五一
        holidays_t = [[pd.to_datetime(start), pd.to_datetime(end)] for start, end in holidays_t]
        conditions = [(merge_df['时间'] >= start) & (merge_df['时间'] <= end) for start, end in holidays_t]
        holidays_hour = merge_df[np.any(conditions, axis=0)]
        holidays_hour_index = self.cal_mape(holidays_hour['流量_x'], holidays_hour['流量_y'])
        return holidays_hour_index
